
const STD_MATRIX3D_DATA = {
    "m00": 1, "m01": 0, "m02": 0, "m03": 0,
    "m10": 0, "m11": 1, "m12": 0, "m13": 0,
    "m20": 0, "m21": 0, "m22": 1, "m23": 0,
    "m30": 0, "m31": 0, "m32": 0, "m33": 1
};

export default class AnimationAtlas {

    constructor(data, atlas) {
        this.parseData(data);
        this._atlas = atlas;
        this._symbolData = {};
        this._symbolPool = {};
        this._imagePool = [];
        this._frameRate = 24;
    }

    get frameRate() {
        return this._frameRate;
    }

    set frameRate(value) {
        this._frameRate = value;
    }

    hasAnimation(name) {
        return this.hasSymbol(name);
    }

    createAnimation(name = null) {
        name = name || this._defaultSymbolName;
        if (!this.hasSymbol(name)) throw new Error("Animation not found: " + name);
        return new Animation(name, this);
    }

    getAnimationNames(prefix = "", out = null) {
        out = out || [];
        Object.keys(this._symbolData).map(name => {
            if (name != Symbol.BITMAP_SYMBOL_NAME && name.indexOf(prefix) == 0) {
                out[out.length] = name;
            }
        });

        out.sort(Array.CASEINSENSITIVE);
        return out;
    }

    getTexture(name) {
        return this._atlas.getTexture(name);
    }

    getImage(texture) {
        if (this._imagePool.length == 0) {
            return new PIXI.Sprite(texture);
        } else {
            let image = this._imagePool.pop();
            image.texture = texture;
            return image;
        }
    }

    putImage(image) {
        this._imagePool[this._imagePool.length] = image;
    }

    hasSymbol(name) {
        return this._symbolData.hasOwnProperty(name);
    }

    getSymbol(name) {
        let pool = this.getSymbolPool(name);
        if (pool.length == 0) {
            return new Symbol(this.getSymbolData(name), this);
        } else {
            return pool.pop();
        }
    }

    putSymbol(symbol) {
        symbol.reset();
        let pool = this.getSymbolPool(symbol.symbolName);
        pool[pool.length] = symbol;
        symbol.currentFrame = 0;
    }

    getSymbolPool(name) {
        let pool = this._symbolPool[name];
        if (pool == null) {
            pool = this._symbolPool[name] = [];
        }
        return pool;
    }

    getSymbolData(name) {
        return this._symbolData[name];
    }

    parseData(data) {
        let metaData = data.metadata;
        if (metaData && metaData.frameRate > 0) {
            this._frameRate = parseInt(metaData.frameRate, 10);
        } else {
            this._frameRate = 24;
        }

        this._symbolData = {};

        Object.keys(data.SYMBOL_DICTIONARY.Symbols).map(key => {
            let symbolData = data.SYMBOL_DICTIONARY.Symbols[key];
            this._symbolData[symbolData.SYMBOL_name] = AnimationAtlas.preprocessSymbolData(symbolData);
        });
        // the main animation
        let defaultSymbolData = AnimationAtlas.preprocessSymbolData(data.ANIMATION);
        this._defaultSymbolName = defaultSymbolData.SYMBOL_name;
        this._symbolData[this._defaultSymbolName] = defaultSymbolData;

        // a purely internal symbol for bitmaps - simplifies their handling
        this._symbolData[Symbol.BITMAP_SYMBOL_NAME] = {
            SYMBOL_name: Symbol.BITMAP_SYMBOL_NAME,
            TIMELINE: { LAYERS: [] }
        };
    }

    static preprocessSymbolData(symbolData) {
        let timeLineData = symbolData.TIMELINE;
        let layerDates = timeLineData.LAYERS;
        if (!timeLineData.sortedForRender) {
            timeLineData.sortedForRender = true;
            layerDates.reverse();
        }

        let numLayers = layerDates.length;
        for (let l = 0; l < numLayers; ++l) {
            let layerData = layerDates[l];
            let frames = layerData.Frames;
            let numFrames = frames.length;
            for (let f = 0; f < numFrames; ++f) {
                let elements = frames[f].elements;
                let numElements = elements.length;

                for (var e = 0; e < numElements; ++e) {
                    var element = elements[e];
                    if (element.hasOwnProperty("ATLAS_SPRITE_instance")) {
                        element = elements[e] = {
                            SYMBOL_Instance: {
                                SYMBOL_name: Symbol.BITMAP_SYMBOL_NAME,
                                Instance_Name: "InstName",
                                bitmap: element.ATLAS_SPRITE_instance,
                                symbolType: SymbolType.GRAPHIC,
                                firstFrame: 0,
                                loop: LoopMode.LOOP,
                                transformationPoint: { x: 0, y: 0 },
                                Matrix3D: STD_MATRIX3D_DATA
                            }
                        }
                    }

                    // not needed - remove decomposed matrix to save some memory
                    delete element.SYMBOL_Instance.DecomposedMatrix;
                }
            }
        }
        return symbolData;
    }
}

AnimationAtlas.ASSET_TYPE = 'animationAtlas';